package com.mspbots.web.security;

import cn.hutool.core.util.RandomUtil;
import com.mspbots.web.params.UserParam;
import com.mspbots.web.security.service.UsersService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import static com.mspbots.web.security.service.AuthService.DEFAULT_TENANT_USER_ROLE_NAME;

/**
 * com.mspbots.web.security.UsersReceiver
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2020/4/20
 */
@Log4j2
@Component
@RequiredArgsConstructor
public class UsersReceiver {

    private final UsersService usersService;
    private final DatabaseClient databaseClient;

    @RabbitListener(queues = "data-teams.register-users")
    public void processUser(User user) {
        if (ObjectUtils.isEmpty(user.getUsername())) {
            return;
        }
        Optional<User> saveUser = this.usersService.loadUserByUsername(user.getUsername())
                .flatMap(oldUser -> {
                    oldUser.setModifiedTime(LocalDateTime.now());
                    oldUser.setFirstName(user.getFirstName());
                    oldUser.setLastName(user.getLastName());
                    oldUser.setEmail(user.getEmail());
                    return this.usersService.save(oldUser);
                }).switchIfEmpty(this.usersService.register(UserParam.builder()
                        .username(user.getUsername())
                        .roles(Set.of(DEFAULT_TENANT_USER_ROLE_NAME))
                        .password(RandomUtil.randomString(8)).tenantId(user.getTenantId()).email(user.getEmail())
                        .firstName(user.getFirstName()).lastName(user.getLastName()).build()))
                .blockOptional();

        saveUser.ifPresent(this::autoMapping);
    }

    private void autoMapping(User user) {
        this.databaseClient.execute("SELECT a.id AS usersId,b.id AS cwUserId,c.teams_user_id AS teamsUserId,d.id AS tsheetUserId " +
                "FROM users a LEFT JOIN tenant_user b ON lower(a.username) = lower(b.email) AND a.tenant_id=b.tenant_id " +
                "LEFT JOIN tenant_teams_user c ON lower(a.username) = lower(c.teams_mail) AND a.tenant_id=c.tenant_id " +
                "LEFT JOIN tsheet_users d ON lower(a.username) = lower(d.email) AND a.tenant_id=d.tenant_id " +
                "WHERE a.tenant_id=:tenantId AND a.id=:id LIMIT 1")
                .bind("id", user.getId())
                .bind("tenantId", user.getTenantId())
                .map((row, rowMetadata) -> {
                    Map<String, Object> resultMap = new HashMap<>(4);
                    resultMap.put("usersId", row.get("usersId"));
                    resultMap.put("cwUserId", row.get("cwUserId"));
                    resultMap.put("teamsUserId", row.get("teamsUserId"));
                    resultMap.put("tsheetUserId", row.get("tsheetUserId"));
                    return resultMap;
                }).all()
                .flatMap(map -> {
                    StringBuilder stringBuffer = new StringBuilder("where tenant_id = " + user.getTenantId());
                    UsersMapping usersMapping = UsersMapping.builder().tenantId(user.getTenantId())
                            .userId(user.getId()).build();
                    stringBuffer.append(" and (user_id=").append(user.getId());
                    if (!ObjectUtils.isEmpty(map.get("cwUserId"))) {
                        stringBuffer.append(" or tenant_user_id =").append(map.get("cwUserId"));
                        usersMapping.setTenantUserId(Long.valueOf(map.get("cwUserId").toString()));
                    }
                    if (!ObjectUtils.isEmpty(map.get("teamsUserId"))) {
                        stringBuffer.append(" or teams_user_id = '").append(map.get("teamsUserId")).append("' ");
                        usersMapping.setTeamsUserId(map.get("teamsUserId").toString());
                    }
                    if (!ObjectUtils.isEmpty(map.get("tsheetUserId"))) {
                        stringBuffer.append(" or sheet_user_id = ").append(map.get("tsheetUserId"));
                        usersMapping.setSheetUserId(Long.valueOf(map.get("tsheetUserId").toString()));
                    }
                    stringBuffer.append(")");

                    return this.databaseClient.execute("SELECT * FROM tenant_user_mapping " + stringBuffer.toString())
                            .as(UsersMapping.class)
                            .fetch().all().flatMap(mapping -> {
                                if (ObjectUtils.isEmpty(mapping.getUserId())) {
                                    mapping.setUserId(user.getId());
                                }
                                return this.databaseClient.update().table(UsersMapping.class)
                                        .using(mapping).fetch().rowsUpdated();
                            }).switchIfEmpty(this.databaseClient.insert().into(UsersMapping.class).using(usersMapping)
                                    .fetch().rowsUpdated());
                }).subscribe(result -> log.info("Register users from user{},result:{}", user, result),
                err -> log.error("Error msg:{}", err.getMessage()));

    }

}
